/*
 *  Copyright (C) 2010:
 *                 Buzzanca Marco <marco.bzn@gmail.com>
 *         Monaco Davide Giuseppe <black.ralkass@gmail.com>
 *              Petralia Veronica <veronicapetralia@gmail.com>
 *
 * This file is part of DS2011BMP
 *
 * DS2011BMP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DS2011BMP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * @file ui.cpp
 * @author Davide Giuseppe Monaco
 * @author Marco Buzzanca
 *
 * This header file contains UI class implementation.
 */

#include "ui.h"

using namespace std;

// Constructors

UI::UI(void)
  : _port(11000) { }

UI::UI(const Address& manager_address)
  : _manager_address(manager_address), _port(11000) { }

UI::UI(const Address& manager_address, const Address& server_address)
  : _manager_address(manager_address), _port(server_address.getPort()) { }

UI::UI(const std::string& manager_IP, boost::uint16_t manager_port,
       boost::uint16_t server_port)
  : _manager_address(manager_IP, manager_port), _port(server_port) { }

// Destructor

UI::~UI(void) { }

// Accessor methods

boost::uint16_t UI::getServerPort() const
{ return _port; }

void UI::setServerPort(boost::uint16_t server_port)
{ _port = server_port; }

const Address& UI::getManagerAddress() const
{ return _manager_address; }

void UI::setManagerAddress(const Address& manager_address)
{ _manager_address = manager_address; }

void UI::setManagerAddress(const std::string& manager_IP,
                           boost::uint16_t manager_port)
{
    _manager_address.setIP(manager_IP);
    _manager_address.setPort(manager_port);
}

void UI::sendFile(void)
{
    using boost::asio::ip::udp;

    for (;;)
    {
      _ft_req req = _file_requests.pop();

      udp::socket socket(_io_service, udp::endpoint(udp::v4(), 0));

      udp::resolver resolver(_io_service);
      udp::resolver::query query(udp::v4(), req.client_IP, req.client_port);
      udp::resolver::iterator iterator(resolver.resolve(query));
      udp::endpoint endpoint = iterator->endpoint();
      std::cout << req.source_file.get() << std::endl;
      std::cout << req.client_IP << ':' << req.client_port << std::endl;
      FILE* fp = fopen(req.source_file.get(), "rb");

      if (fp == NULL)
        return;

      char buf[1024];
      size_t file_size, received_size;

      // Gets file size.
      fseek(fp, 0L, SEEK_END);
      file_size = ftell(fp);

      // Submits file size, so the receiver knows how many packets will
      // be sent.
      socket.send_to
        (boost::asio::buffer(&file_size, sizeof(size_t)), endpoint);

      socket.receive_from
        (boost::asio::buffer(&received_size, sizeof(size_t)), endpoint);

      if (file_size != received_size)
      {
        fclose(fp);
        return;
      }

      // Sets file pointer to the beginning of the file.
      fseek(fp, 0, SEEK_SET);

      size_t bytes_read;

      while (feof(fp) == 0)
      {
        bytes_read = fread(buf, sizeof(char), 1024, fp);
        socket.send_to(boost::asio::buffer(buf, bytes_read), endpoint);
      }

      fclose(fp);
    }
}

void UI::run(void)
{
  using boost::asio::ip::udp;

  // Generates dummy workflow for the workers.
  boost::thread t1(&UI::generateWorkflow, this);

  // Dequeues file transfer requests, and processes them.
  boost::thread t2(&UI::sendFile, this);

  udp::socket socket(_io_service, udp::endpoint(udp::v4(), _port));
  udp::endpoint endpoint;

  std::cout << "File transfer server ready..." << std::endl;

  for (;;)
  {
    // Receives filename.
    char recv_buf[UDPReceiver::MAX_BUF_LEN];

    size_t recv_len = socket.receive_from
      (boost::asio::buffer(recv_buf, UDPReceiver::MAX_BUF_LEN),
       endpoint);

    // Buffer might be dirty, this ensures the string is properly
    // terminated.
    recv_buf[recv_len] = '\0';

    _ft_req req;

    req.source_file = boost::shared_ptr<char>(new char[recv_len + 1]);
    req.client_IP = endpoint.address().to_string();
    req.client_port = "15000"; // TODO: Custom UI port.

    memcpy(req.source_file.get(), recv_buf, recv_len + 1);

    _file_requests.push(req);
  }

  // Technically never reached.
  t1.join();
  t2.join();
}

void UI::generateWorkflow(void)
{
  // setAddr() is not called because default address is used.
  wf_queue_manager_proxy_t proxy = _wfqp_factory.getProxy();

  int sleep_secs = rand() % 10 + 1;

  cout << "UI Sleep time set to: " << sleep_secs << " seconds." << endl;

  int max_tasks, i;

  for (;;)
  {
    // XXX: use this random number to assign Workflow an ID, it's dummy
    max_tasks = rand() % 7 + 1;
    Workflow w(max_tasks);

    cout << "Workflow will consist of: " << max_tasks << " tasks." << endl;

    for (i = 0; i < max_tasks; i++)
    {
      TaskDescriptor td(i);

      switch (rand() % 4)
      {
        default:
          td.setType(Executable);
          td.setCommand("./dummy1");
          td.setLink("./dummy1");
          break;

        case 1:
          td.setType(Executable);
          td.setCommand("./dummy2 10 76");
          td.setLink("./dummy2");
          break;

        case 2:
          td.setType(scriptBash);
          td.setCommand("./dummy3.sh");
          td.setLink("./dummy3.sh");
          break;

        case 3:
          td.setType(scriptBash);
          td.setCommand("./dummy4.sh hello world");
          td.setLink("./dummy4.sh");
          break;
      }

      cout << "Adding Task" << endl;

      w.addTask(td);
    }

    // XXX: print out the WF
    cout << "Submitting workflow:\n" << w << endl;
    proxy->enqueue(w);

    cout << "Sleeping . . ." << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(sleep_secs));

    cout << "Awakened!" << endl;
  }
}
